////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxifecrop10.cpp
/// @brief CAMXIFECROP10 class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxifecrop10.h"

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::Create(
    IFEModuleCreateData* pCreateData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pCreateData)
    {
        IFEPipelinePath modulePath = pCreateData->pipelineData.IFEPath;

        if ((IFEPipelinePath::FDPath           == modulePath) ||
            (IFEPipelinePath::FullPath         == modulePath) ||
            (IFEPipelinePath::DS4Path          == modulePath) ||
            (IFEPipelinePath::DS16Path         == modulePath) ||
            (IFEPipelinePath::PixelRawDumpPath == modulePath))
        {
            pCreateData->pModule = CAMX_NEW IFECrop10(pCreateData);
            if (NULL == pCreateData->pModule)
            {
                result = CamxResultENoMemory;
                CAMX_ASSERT_ALWAYS_MESSAGE("Memory allocation failed");
            }
        }
        else
        {
            result = CamxResultEInvalidArg;
            CAMX_LOG_ERROR(CamxLogGroupISP, "Invalid IFE Pipeline Path");
        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_LOG_ERROR(CamxLogGroupISP, "Input Null pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::GetRegCmd
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* IFECrop10::GetRegCmd()
{
    return &m_regCmd;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::GetChromaSubsampleFactor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::GetChromaSubsampleFactor(
    FLOAT* pHorizontalSubSampleFactor,
    FLOAT* pVerticalSubsampleFactor)
{
    CamxResult result = CamxResultSuccess;

    if ((NULL != pHorizontalSubSampleFactor) &&
        (NULL != pVerticalSubsampleFactor))
    {
        switch (m_pixelFormat)
        {
            /// @todo (CAMX-526) Check for all the formats and subsample factors
            case Format::YUV420NV12TP10:
            case Format::YUV420NV21TP10:
            case Format::YUV422NV16TP10:
            case Format::YUV420NV12:
            case Format::YUV420NV21:
            case Format::YUV422NV16:
            case Format::UBWCTP10:
            case Format::UBWCNV124R:
            case Format::PD10:
            case Format::FDYUV420NV12:
            case Format::P010:
                *pHorizontalSubSampleFactor = 1.0f;
                *pVerticalSubsampleFactor   = 2.0f;
                break;

            case Format::Y8:
            case Format::Y16:
            case Format::FDY8:
                break;
            default:
                CAMX_LOG_ERROR(CamxLogGroupISP, "Incompatible Format: %d, path %d",  m_pixelFormat, m_modulePath);
                *pHorizontalSubSampleFactor = 1.0f;
                *pVerticalSubsampleFactor   = 1.0f;
                result = CamxResultEInvalidArg;
                break;
        }
    }
    else
    {
        CAMX_ASSERT_ALWAYS_MESSAGE("Input Null pointer");
        result = CamxResultEFailed;
    }

    return  result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ModifyCropWindow
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::ModifyCropWindow(
    CropType cropType)
{
    CamxResult result            = CamxResultSuccess;
    FLOAT      outputAspectRatio;
    FLOAT      cropAspectRatio;
    UINT32     temporaryValue;

    CAMX_UNREFERENCED_PARAM(cropType);

    CAMX_ASSERT_MESSAGE(0 != m_pState->streamDimension.height, "Invalid output height");
    CAMX_ASSERT_MESSAGE(0 != m_pState->modifiedCropWindow.height, "Invalid Crop window height");

    outputAspectRatio = static_cast<FLOAT>(m_pState->streamDimension.width) / m_pState->streamDimension.height;
    cropAspectRatio   = static_cast<FLOAT>(m_pState->modifiedCropWindow.width) / m_pState->modifiedCropWindow.height;

    CAMX_LOG_VERBOSE(CamxLogGroupISP, "output m_output %d, MNDS Output %d * %d, Crop Window [%d, %d, %d, % d]",
                     m_output,
                     m_pState->scalerOutput.dimension.width,
                     m_pState->scalerOutput.dimension.height,
                     m_pState->modifiedCropWindow.left,
                     m_pState->modifiedCropWindow.top,
                     m_pState->modifiedCropWindow.width,
                     m_pState->modifiedCropWindow.height);

    // Modify crop window based on the aspect ratio, Crop window should match the output
    if (Utils::AbsoluteFLOAT(outputAspectRatio - cropAspectRatio) > AspectRatioTolerance)
    {
        if (outputAspectRatio > cropAspectRatio)
        {
            temporaryValue              = m_pState->modifiedCropWindow.height;
            m_pState->modifiedCropWindow.height =
                Utils::FloorUINT32(2, (static_cast<UINT32>(m_pState->modifiedCropWindow.width / outputAspectRatio)));
            m_pState->modifiedCropWindow.top    =
                m_pState->modifiedCropWindow.top + ((temporaryValue - m_pState->modifiedCropWindow.height) >> 1);
        }
        else
        {
            temporaryValue             = m_pState->modifiedCropWindow.width;
            m_pState->modifiedCropWindow.width =
                Utils::Utils::FloorUINT32(2, (static_cast<UINT32>(m_pState->modifiedCropWindow.height * outputAspectRatio)));
            if (CropTypeFromRight == cropType)
            {
                m_pState->modifiedCropWindow.left = m_pState->modifiedCropWindow.left;
            }
            else if (CropTypeFromLeft == cropType)
            {
                m_pState->modifiedCropWindow.left  =
                    m_pState->modifiedCropWindow.left + (temporaryValue - m_pState->modifiedCropWindow.width);
            }
            else
            {
                m_pState->modifiedCropWindow.left  =
                    m_pState->modifiedCropWindow.left + ((temporaryValue - m_pState->modifiedCropWindow.width) >> 1);
            }
        }
    }

    CAMX_LOG_VERBOSE(CamxLogGroupISP, "Modified Crop Window [%d, %d, %d, % d]",
                     m_pState->modifiedCropWindow.left,
                     m_pState->modifiedCropWindow.top,
                     m_pState->modifiedCropWindow.width,
                     m_pState->modifiedCropWindow.height);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureFDLumaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureFDLumaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.FDLuma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.FDLuma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.FDLuma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.FDLuma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureFDChromaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureFDChromaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.FDChroma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.FDChroma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.FDChroma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.FDChroma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureFullLumaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureFullLumaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.fullLuma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.fullLuma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.fullLuma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.fullLuma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureFullChromaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureFullChromaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.fullChroma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.fullChroma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.fullChroma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.fullChroma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureDS4LumaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureDS4LumaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.DS4Luma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.DS4Luma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.DS4Luma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.DS4Luma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureDS4ChromaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureDS4ChromaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.DS4Chroma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.DS4Chroma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.DS4Chroma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.DS4Chroma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureDS16LumaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureDS16LumaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.DS16Luma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.DS16Luma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.DS16Luma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.DS16Luma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigureDS16ChromaRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigureDS16ChromaRegisters(
    CropInfo* pCrop)
{
    m_regCmd.DS16Chroma.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.DS16Chroma.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.DS16Chroma.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.DS16Chroma.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ConfigurePixelRawRegisters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::ConfigurePixelRawRegisters(
    CropInfo* pCrop)
{
    m_regCmd.pixelRaw.pixelConfig.bitfields.FIRST_PIXEL = pCrop->firstPixel;
    m_regCmd.pixelRaw.pixelConfig.bitfields.LAST_PIXEL  = pCrop->lastPixel;
    m_regCmd.pixelRaw.lineConfig.bitfields.FIRST_LINE   = pCrop->firstLine;
    m_regCmd.pixelRaw.lineConfig.bitfields.LAST_LINE    = pCrop->lastLine;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::CalculateCropCoordinates
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::CalculateCropCoordinates(
    CropInfo*   pCrop,
    CropType    cropType)
{
    UINT32 cropLeft;
    UINT32 cropTop;
    UINT32 cropWidth;
    UINT32 cropHeight;
    UINT32 offCenterCoordinateX;
    UINT32 offCenterCoordinateY;
    UINT32 centerCoordinateX;
    UINT32 centerCoordinateY;
    UINT32 temporaryValue;

    CropInfo precalculatedCrop = *pCrop;

    m_pState->cropInfo.left   = 0;
    m_pState->cropInfo.top    = 0;
    m_pState->cropInfo.width  = m_pState->streamDimension.width;
    m_pState->cropInfo.height = m_pState->streamDimension.height;

    m_pState->appliedCropInfo.left   = 0;
    m_pState->appliedCropInfo.top    = 0;
    m_pState->appliedCropInfo.width  = m_pState->streamDimension.width;
    m_pState->appliedCropInfo.height = m_pState->streamDimension.height;

    CAMX_ASSERT_MESSAGE(0 != m_pState->scalerOutput.scalingFactor, "Invalid Scale factor");

    // Map crop window on scaler output
    cropLeft   = Utils::EvenFloorUINT32(static_cast<UINT32>(m_pState->modifiedCropWindow.left   /
                        m_pState->scalerOutput.scalingFactor));
    cropTop    = Utils::EvenFloorUINT32(static_cast<UINT32>(m_pState->modifiedCropWindow.top    /
                        m_pState->scalerOutput.scalingFactor));
    cropWidth  = Utils::EvenFloorUINT32(static_cast<UINT32>(m_pState->modifiedCropWindow.width  /
                        m_pState->scalerOutput.scalingFactor));
    cropHeight = Utils::EvenFloorUINT32(static_cast<UINT32>(m_pState->modifiedCropWindow.height /
                        m_pState->scalerOutput.scalingFactor));

    CAMX_LOG_VERBOSE(CamxLogGroupISP, "Path %d, Crop Map [%d , %d, %d, %d], Scale factor %f\n",
                     m_output,
                     cropLeft,
                     cropTop,
                     cropWidth,
                     cropHeight,
                     m_pState->scalerOutput.scalingFactor);

    CAMX_LOG_VERBOSE(CamxLogGroupISP, "Path %d, Crop Window [%d , %d, %d, %d], Scale factor %f\n",
                     m_output,
                     m_pState->modifiedCropWindow.left,
                     m_pState->modifiedCropWindow.top,
                     m_pState->modifiedCropWindow.width,
                     m_pState->modifiedCropWindow.height,
                     m_pState->scalerOutput.scalingFactor);

    CAMX_LOG_VERBOSE(CamxLogGroupISP, "Path %d, Scaler output[%d * %d], IFE output [%d * %d], Scale factor %f\n",
                     m_output,
                     m_pState->scalerOutput.dimension.width,
                     m_pState->scalerOutput.dimension.height,
                     m_pState->streamDimension.width,
                     m_pState->streamDimension.height,
                     m_pState->scalerOutput.scalingFactor);

    // Calculate the pixel co-ordinates for crop
    if ((cropWidth >= m_pState->streamDimension.width) &&
        (cropWidth <= m_pState->scalerOutput.dimension.width))
    {
        // Analog zoom case
        if (CropTypeFromRight == cropType)
        {
            pCrop->firstPixel = Utils::EvenFloorUINT32(cropLeft);
            pCrop->lastPixel  = pCrop->firstPixel + m_pState->streamDimension.width - 1;
        }
        else if (CropTypeFromLeft == cropType)
        {
            pCrop->firstPixel = Utils::EvenFloorUINT32(cropLeft + (cropWidth - m_pState->streamDimension.width));
            pCrop->lastPixel  = pCrop->firstPixel + m_pState->streamDimension.width - 1;
        }
        else
        {
            pCrop->firstPixel = Utils::FloorUINT32(2, (cropLeft + ((cropWidth - m_pState->streamDimension.width) / 2)));
            pCrop->lastPixel  = pCrop->firstPixel + m_pState->streamDimension.width - 1;
        }
    }
    else
    {
        // Digital zoom case
        /// @todo (CAMX-2121) Handle digital zoom properly (for offcenter cases / dual IFE cases)
        if (CropTypeFromRight == cropType)
        {
            pCrop->firstPixel = Utils::EvenFloorUINT32(cropLeft);
            pCrop->lastPixel  = pCrop->firstPixel + m_pState->streamDimension.width - 1;

            // Beyond IFE scaling range IPE cropping needed for zoom
            m_pState->cropInfo.left  = 0;
            m_pState->cropInfo.width = cropWidth;
        }
        else if (CropTypeFromLeft == cropType)
        {
            UINT32 cropRight        = cropLeft + cropWidth;
            offCenterCoordinateX    =
                (cropRight > m_pState->streamDimension.width) ? (cropRight - m_pState->streamDimension.width) : 0;

            pCrop->firstPixel = Utils::FloorUINT32(2, offCenterCoordinateX);
            pCrop->lastPixel  = pCrop->firstPixel + m_pState->streamDimension.width - 1;

            // Beyond IFE scaling range IPE cropping needed for zoom
            m_pState->cropInfo.left  = cropLeft - pCrop->firstPixel;
            m_pState->cropInfo.width = cropWidth;
        }
        else
        {
            centerCoordinateX  =
                Utils::FloorUINT32(2, ((m_pState->scalerOutput.dimension.width - m_pState->streamDimension.width) / 2));

            // Minimum to make sure the 1st pixel of crop window falls within the output of FOV module
            offCenterCoordinateX = Utils::MinUINT32(centerCoordinateX, cropLeft);

            // make sure the last pixel of crop window falls within the output of FOV module
            if ((offCenterCoordinateX + m_pState->streamDimension.width) < (cropLeft + cropWidth))
            {
                // Distance from the last pixel of the input to the last pixel of the crop window
                temporaryValue       = m_pState->scalerOutput.dimension.width - (cropLeft + cropWidth);
                // Move the coordinate furthuer to have the crop window in the FOV output
                offCenterCoordinateX = offCenterCoordinateX + (centerCoordinateX - temporaryValue);
            }

            pCrop->firstPixel = Utils::FloorUINT32(2, offCenterCoordinateX);
            if ((pCrop->firstPixel > m_pState->scalerOutput.dimension.width) ||
                (pCrop->firstPixel + m_pState->streamDimension.width >
                m_pState->scalerOutput.dimension.width))
            {
                pCrop->firstPixel = Utils::FloorUINT32(2, centerCoordinateX);
            }

            pCrop->lastPixel  = pCrop->firstPixel + m_pState->streamDimension.width - 1;

            // Beyond IFE scaling range IPE cropping needed for zoom
            m_pState->cropInfo.left  = cropLeft - pCrop->firstPixel;
            m_pState->cropInfo.width = cropWidth;
        }
    }

    // Calculate the line co-ordinates for crop
    if ((cropHeight >= m_pState->streamDimension.height) &&
        (cropHeight <= m_pState->scalerOutput.dimension.height))
    {
        pCrop->firstLine = Utils::FloorUINT32(2, (cropTop + ((cropHeight - m_pState->streamDimension.height) / 2)));
        pCrop->lastLine  = pCrop->firstLine + m_pState->streamDimension.height - 1;
    }
    else
    {
        // Digital zoom case
        // First line if we consider center zoom
        centerCoordinateY  =
            Utils::FloorUINT32(2, ((m_pState->scalerOutput.dimension.height - m_pState->streamDimension.height) / 2));

        // Minimum to make sure the 1st line of crop window falls within the output of FOV module
        offCenterCoordinateY = Utils::MinUINT32(centerCoordinateY, cropTop);

        // make sure the last line of crop window falls within the output of FOV module
        if ((offCenterCoordinateY + m_pState->streamDimension.height) < (cropTop + cropHeight))
        {
            // Distance from the last line of the input to the last line of the crop window
            temporaryValue       = m_pState->scalerOutput.dimension.height - (cropTop + cropHeight);
            // Move the coordinate furthuer to have the crop window in the FOV output
            offCenterCoordinateY = offCenterCoordinateY + (centerCoordinateY - temporaryValue);
        }

        pCrop->firstLine = Utils::FloorUINT32(2, offCenterCoordinateY);

        if ((pCrop->firstLine > m_pState->scalerOutput.dimension.height) ||
            (pCrop->firstLine + m_pState->streamDimension.height >
            m_pState->scalerOutput.dimension.height))
        {
            pCrop->firstLine = Utils::FloorUINT32(2, centerCoordinateY);
        }

        pCrop->lastLine  = pCrop->firstLine + m_pState->streamDimension.height - 1;

        // Beyond IFE scaling range IPE cropping needed for zoom
        m_pState->cropInfo.top    = cropTop - pCrop->firstLine;
        m_pState->cropInfo.height = cropHeight;
    }

    if (TRUE == m_pInputData->pStripeConfig->overwriteStripes)
    {
        pCrop->firstPixel = precalculatedCrop.firstPixel;
        pCrop->lastPixel = precalculatedCrop.lastPixel;
        pCrop->firstLine = precalculatedCrop.firstLine;
        pCrop->lastLine = precalculatedCrop.lastLine;
    }

    if ((TRUE == m_pInputData->pStripeConfig->overwriteStripes) &&
        ((DS4Output == m_output) || (DS16Output == m_output)))
    {
        pCrop->firstLine = precalculatedCrop.firstLine;
        pCrop->lastLine  = precalculatedCrop.lastLine;
    }

    if (Utils::AbsoluteFLOAT(m_pState->scalerOutput.scalingFactor) <= IFECropScalingThreshold)
    {
        m_pState->appliedCropInfo.left   = pCrop->firstPixel;
        m_pState->appliedCropInfo.top    = pCrop->firstLine;
        m_pState->appliedCropInfo.width  = (pCrop->lastPixel - pCrop->firstPixel) + 1;
        m_pState->appliedCropInfo.height = (pCrop->lastLine - pCrop->firstLine) + 1;
    }
    else
    {
        m_pState->appliedCropInfo.left   = m_pState->modifiedCropWindow.left;
        m_pState->appliedCropInfo.top    = m_pState->modifiedCropWindow.top;
        m_pState->appliedCropInfo.width  = m_pState->modifiedCropWindow.width;
        m_pState->appliedCropInfo.height = m_pState->modifiedCropWindow.height;
    }

    CAMX_LOG_VERBOSE(CamxLogGroupISP, "Path %d, Luma Crop window [%d, %d, %d, % d]",
                     m_output,
                     pCrop->firstPixel,
                     pCrop->lastPixel,
                     pCrop->firstLine,
                     pCrop->lastLine);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::CalculateCropInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::CalculateCropInfo(
    CropInfo*   pLumaCrop,
    CropInfo*   pChromaCrop,
    CropType    cropType)
{
    FLOAT      horizontalSubSampleFactor = 1.0f;
    FLOAT      verticalSubsampleFactor   = 1.0f;
    CamxResult result                    = CamxResultSuccess;

    CAMX_ASSERT(NULL != pLumaCrop);
    CAMX_ASSERT(NULL != pChromaCrop);

    // Core calculation of crop baased on FOV
    CalculateCropCoordinates(pLumaCrop, cropType);

    result = GetChromaSubsampleFactor(&horizontalSubSampleFactor, &verticalSubsampleFactor);

    CAMX_ASSERT_MESSAGE(0 != horizontalSubSampleFactor, "Invalid horizontalSubSampleFactor");
    CAMX_ASSERT_MESSAGE(0 != horizontalSubSampleFactor, "Invalid verticalSubsampleFactor");

    if (CamxResultSuccess == result)
    {
        if (FALSE == m_pInputData->pStripeConfig->overwriteStripes)
        {
            pChromaCrop->firstPixel = static_cast<UINT32>(pLumaCrop->firstPixel / horizontalSubSampleFactor);
            pChromaCrop->lastPixel = pChromaCrop->firstPixel +
                (static_cast<UINT32>(m_pState->streamDimension.width / horizontalSubSampleFactor)) - 1;
        }
        pChromaCrop->firstLine = static_cast<UINT32>(pLumaCrop->firstLine / verticalSubsampleFactor);
        pChromaCrop->lastLine  = pChromaCrop->firstLine +
            (static_cast<UINT32>(m_pState->streamDimension.height / verticalSubsampleFactor)) - 1;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::CreateCmdList
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::CreateCmdList(
    ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;
    UINT32     register1;
    UINT32     numberOfValues1;
    UINT32*    pValues1;
    UINT32     register2;
    UINT32     numberOfValues2;
    UINT32*    pValues2;

    if (NULL != pInputData->pCmdBuffer)
    {
        CmdBuffer* pCmdBuffer = pInputData->pCmdBuffer;

        switch (m_output)
        {
            case FDOutput:
                register1       = regIFE_IFE_0_VFE_FD_OUT_Y_CROP_LINE_CFG;
                numberOfValues1 = sizeof(IFECrop10FDLumaReg) / RegisterWidthInBytes;
                pValues1        = reinterpret_cast<UINT32*>(&m_regCmd.FDLuma);

                register2       = regIFE_IFE_0_VFE_FD_OUT_C_CROP_LINE_CFG;
                numberOfValues2 = sizeof(IFECrop10FDChromaReg) / RegisterWidthInBytes;
                pValues2        = reinterpret_cast<UINT32*>(&m_regCmd.FDChroma);
                break;

            case FullOutput:
                register1       = regIFE_IFE_0_VFE_FULL_OUT_Y_CROP_LINE_CFG;
                numberOfValues1 = (sizeof(IFECrop10FullLumaReg) / RegisterWidthInBytes);
                pValues1        = reinterpret_cast<UINT32*>(&m_regCmd.fullLuma);

                register2       = regIFE_IFE_0_VFE_FULL_OUT_C_CROP_LINE_CFG;
                numberOfValues2 = sizeof(IFECrop10FullChromaReg) / RegisterWidthInBytes;
                pValues2        = reinterpret_cast<UINT32*>(&m_regCmd.fullChroma);
                break;

            case DS4Output:
                register1       = regIFE_IFE_0_VFE_DS4_Y_CROP_LINE_CFG;
                numberOfValues1 = sizeof(IFECrop10DS4LumaReg) / RegisterWidthInBytes;
                pValues1        = reinterpret_cast<UINT32*>(&m_regCmd.DS4Luma);

                register2       = regIFE_IFE_0_VFE_DS4_C_CROP_LINE_CFG;
                numberOfValues2 = sizeof(IFECrop10DS4ChromaReg) / RegisterWidthInBytes;
                pValues2        = reinterpret_cast<UINT32*>(&m_regCmd.DS4Chroma);;
                break;

            case DS16Output:
                register1       = regIFE_IFE_0_VFE_DS16_Y_CROP_LINE_CFG;
                numberOfValues1 = sizeof(IFECrop10DS16LumaReg) / RegisterWidthInBytes;
                pValues1        = reinterpret_cast<UINT32*>(&m_regCmd.DS16Luma);

                register2       = regIFE_IFE_0_VFE_DS16_C_CROP_LINE_CFG;
                numberOfValues2 = sizeof(IFECrop10DS16ChromaReg) / RegisterWidthInBytes;
                pValues2        = reinterpret_cast<UINT32*>(&m_regCmd.DS16Chroma);
                break;

            case PixelRawOutput:
                register1       = regIFE_IFE_0_VFE_CROP_PIXEL_RAW_DUMP_WIDTH_CFG;
                numberOfValues1 = sizeof(IFECropPixelRawReg) / RegisterWidthInBytes;
                pValues1        = reinterpret_cast<UINT32*>(&m_regCmd.pixelRaw);
                numberOfValues2 = 0;
                break;

            default:
                CAMX_ASSERT_ALWAYS_MESSAGE("We would never runinto this case");
                return CamxResultEInvalidState;
                break;
        }

        result = PacketBuilder::WriteRegRange(pCmdBuffer, register1, numberOfValues1, pValues1);

        if ((CamxResultSuccess == result) && (numberOfValues2 > 0))
        {
            result = PacketBuilder::WriteRegRange(pCmdBuffer, register2, numberOfValues2, pValues2);
        }

        if (CamxResultSuccess != result)
        {
            CAMX_ASSERT_ALWAYS_MESSAGE("Failed to configure Crop Register for path %d", m_output);
        }
    }
    else
    {
        result = CamxResultEInvalidPointer;
        CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Input pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::ValidateDependenceParams
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::ValidateDependenceParams(
    ISPInputData* pInputData)
{
    UINT32           CAMIFWidth;
    UINT32           CAMIFHeight;
    CamxResult       result                  = CamxResultSuccess;
    CropInfo*        pSensorCAMIFCrop        = &pInputData->pStripeConfig->CAMIFCrop;
    CropWindow*      pInputCropWindow        = &pInputData->pStripeConfig->HALCrop[m_output];
    StreamDimension* pHALStream              = &pInputData->pStripeConfig->stream[m_output];
    IFEScalerOutput* pCalculatedScalerOutput = &pInputData->pCalculatedData->scalerOutput[m_output];

    if ((0 != pHALStream->width) && (0 != pHALStream->height))
    {
        CAMIFWidth  = pSensorCAMIFCrop->lastPixel - pSensorCAMIFCrop->firstPixel + 1;
        CAMIFHeight = pSensorCAMIFCrop->lastLine - pSensorCAMIFCrop->firstLine + 1;

        // CAMIF input width for YUV sensor would be twice that of sensor width, as each pixel accounts for Y and U/V data
        if (FALSE == pInputData->sensorData.isBayer)
        {
            CAMIFWidth >>= 1;
        }

        // Validate Crop window from HAL
        if ((pInputCropWindow->left + pInputCropWindow->width >  CAMIFWidth)  ||
            (pInputCropWindow->top + pInputCropWindow->height >  CAMIFHeight) ||
            (0 == pInputCropWindow->width)                                    ||
            (0 == pInputCropWindow->height)                                   ||
            (0 == CAMIFWidth)                                                 ||
            (0 == CAMIFHeight))
        {
            CAMX_LOG_ERROR(CamxLogGroupISP, "Invalid Crop window %d, %d, %d, %d, input dimension %d * %d",
                           pInputCropWindow->left,
                           pInputCropWindow->top,
                           pInputCropWindow->width,
                           pInputCropWindow->height,
                           CAMIFWidth, CAMIFHeight);
            result = CamxResultEInvalidArg;
            CAMX_ASSERT_ALWAYS();
        }

        // Scaler output Validation for Full, FD, DS4, DS16 path
        if ((CamxResultSuccess == result)                                   &&
            (0                 != pHALStream->width)                        &&
            (0                 != pHALStream->height)                       &&
            ((PixelRawOutput   != m_output) &&
            ((TRUE             == Utils::FEqual(pCalculatedScalerOutput->scalingFactor, 0.0f)) ||
             (0                == pCalculatedScalerOutput->dimension.width)                    ||
             (0                == pCalculatedScalerOutput->dimension.height))))
        {
            result = CamxResultEInvalidArg;
            CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Scaler ouput for output path %d", m_output);
        }

    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::DumpRegConfig
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::DumpRegConfig()
{
    if (FDOutput == m_output)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupISP, "FD Luma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.FDLuma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.FDLuma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.FDLuma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.FDLuma.lineConfig.bitfields.LAST_LINE);

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "FD Chroma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.FDChroma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.FDChroma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.FDChroma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.FDChroma.lineConfig.bitfields.LAST_LINE);
    }

    if (FullOutput == m_output)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupISP, "Full Luma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.fullLuma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.fullLuma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.fullLuma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.fullLuma.lineConfig.bitfields.LAST_LINE);

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "Full Chroma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.fullChroma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.fullChroma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.fullChroma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.fullChroma.lineConfig.bitfields.LAST_LINE);
    }

    if (DS4Output == m_output)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupISP, "DS4 Luma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.DS4Luma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.DS4Luma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.DS4Luma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.DS4Luma.lineConfig.bitfields.LAST_LINE);

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "DS4 Chroma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.DS4Chroma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.DS4Chroma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.DS4Chroma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.DS4Chroma.lineConfig.bitfields.LAST_LINE);

    }

    if (DS16Output == m_output)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupISP, "DS16 Luma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.DS16Luma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.DS16Luma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.DS16Luma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.DS16Luma.lineConfig.bitfields.LAST_LINE);

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "DS16 Chroma Crop Config[%d, %d, %d, %d]",
                         m_regCmd.DS16Chroma.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.DS16Chroma.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.DS16Chroma.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.DS16Chroma.lineConfig.bitfields.LAST_LINE);
    }

    if (PixelRawOutput == m_output)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupISP, "pixel raw Crop Config[%d, %d, %d, %d]",
                         m_regCmd.pixelRaw.pixelConfig.bitfields.FIRST_PIXEL,
                         m_regCmd.pixelRaw.lineConfig.bitfields.FIRST_LINE,
                         m_regCmd.pixelRaw.pixelConfig.bitfields.LAST_PIXEL,
                         m_regCmd.pixelRaw.lineConfig.bitfields.LAST_LINE);
    }

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::CheckDependenceChange
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL IFECrop10::CheckDependenceChange(
    ISPInputData* pInputData)
{
    BOOL                 result                  = FALSE;
    IFEScalerOutput*     pCalculatedScalerOutput = &pInputData->pCalculatedData->scalerOutput[m_output];
    CropWindow*          pInputCropWindow        = &pInputData->pStripeConfig->HALCrop[m_output];
    ISPHALConfigureData* pHALData                = &pInputData->HALData;
    StreamDimension*     pHALStream              = &pInputData->pStripeConfig->stream[m_output];

    // Generic dependency  check
    if ((pInputCropWindow->left     != m_pState->cropWindow.left)       ||
        (pInputCropWindow->top      != m_pState->cropWindow.top)        ||
        (pInputCropWindow->width    != m_pState->cropWindow.width)      ||
        (pInputCropWindow->height   != m_pState->cropWindow.height)     ||
        (pHALStream->width          != m_pState->streamDimension.width) ||
        (pHALStream->height         != m_pState->streamDimension.height)||
        (pHALData->format[m_output] != m_pixelFormat))
    {
        if ((0 != pHALStream->width) && (0 != pHALStream->height))
        {
            // App data
            m_pState->cropWindow                    = *pInputCropWindow;
            m_pState->modifiedCropWindow            = *pInputCropWindow;
            m_pixelFormat                           = pHALData->format[m_output];
            m_pState->streamDimension.width         = pHALStream->width;
            m_pState->streamDimension.height        = pHALStream->height;

            // Internal data
            m_pState->scalerOutput.dimension.width  = pCalculatedScalerOutput->dimension.width;
            m_pState->scalerOutput.dimension.height = pCalculatedScalerOutput->dimension.height;
            m_pState->scalerOutput.scalingFactor    = pCalculatedScalerOutput->scalingFactor;

            // result
            m_moduleEnable = TRUE;
            result         = TRUE;
        }
        else
        {
            m_moduleEnable = FALSE;
            result         = FALSE;
        }
    }

    if (TRUE == pInputData->pStripeConfig->overwriteStripes)
    {
        ISPInternalData* pFrameLevel = pInputData->pStripeConfig->pFrameLevelData->pFrameData;
        if (TRUE == pFrameLevel->cropDependencyChanged[m_output])
        {
            m_moduleEnable = TRUE;
            result = TRUE;
        }
    }

    pInputData->pCalculatedData->cropDependencyChanged[m_output] = result;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::RunCalculation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::RunCalculation(
    ISPInputData* pInputData)
{
    CamxResult  result          = CamxResultSuccess;
    CropInfo    lumaCrop        = {0, 0, 0, 0};
    CropInfo    chromaCrop      = {0, 0, 0, 0};

    if (PixelRawOutput == m_output)
    {
        lumaCrop.firstPixel = pInputData->pStripeConfig->HALCrop[PixelRawOutput].left;
        lumaCrop.lastPixel = pInputData->pStripeConfig->HALCrop[PixelRawOutput].left +
           pInputData->pStripeConfig->HALCrop[PixelRawOutput].width - 1;
        lumaCrop.firstLine   = 0;
        lumaCrop.lastLine    = pInputData->pStripeConfig->HALCrop[PixelRawOutput].height - 1;
    }

    if (TRUE == m_pInputData->pStripeConfig->overwriteStripes)
    {
        if (((CropTypeFromLeft == pInputData->pStripeConfig->cropType) ||
            (CropTypeFromRight == pInputData->pStripeConfig->cropType)))
        {
            if (FullOutput == m_output)
            {
                ISPInternalData*    pFrameLevel = pInputData->pStripeConfig->pFrameLevelData->pFrameData;
                lumaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_full_Y.firstOut;
                lumaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_full_Y.lastOut;
                lumaCrop.firstLine = pFrameLevel->fullOutCrop.firstLine;
                lumaCrop.lastLine = pFrameLevel->fullOutCrop.lastLine;
                chromaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_full_C.firstOut * 2;
                chromaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_full_C.lastOut * 2 + 1;
                chromaCrop.firstLine = pFrameLevel->fullOutCrop.firstLine / 2;
                chromaCrop.lastLine = (pFrameLevel->fullOutCrop.lastLine + 1) / 2;
            }
            else if (FDOutput == m_output)
            {
                ISPInternalData*    pFrameLevel = pInputData->pStripeConfig->pFrameLevelData->pFrameData;
                lumaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_fd_Y.firstOut;
                lumaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_fd_Y.lastOut;
                lumaCrop.firstLine = pFrameLevel->fdOutCrop.firstLine;
                lumaCrop.lastLine = pFrameLevel->fdOutCrop.lastLine;
                chromaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_fd_C.firstOut * 2;
                chromaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_fd_C.lastOut * 2 + 1;
                chromaCrop.firstLine = pFrameLevel->fullOutCrop.firstLine / 2;
                chromaCrop.lastLine = (pFrameLevel->fullOutCrop.lastLine + 1) / 2;
            }
            else if (DS4Output == m_output)
            {
                lumaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to4_Y.firstOut;
                lumaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to4_Y.lastOut;
                chromaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to4_C.firstOut * 2;
                chromaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to4_C.lastOut * 2 + 1;

                lumaCrop.firstLine   = 0;
                lumaCrop.lastLine    = Utils::AlignGeneric32(pInputData->pStripeConfig->stateDS[DS4Output].MNDSOutput.height,
                    DS4Factor * 2 ) / DS4Factor - 1;
                chromaCrop.firstLine = 0;
                chromaCrop.lastLine  = ((lumaCrop.lastLine + 1) / 2)  - 1;

            }
            else if (DS16Output == m_output)
            {
                lumaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to16_Y.firstOut;
                lumaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to16_Y.lastOut;
                chromaCrop.firstPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to16_C.firstOut * 2;
                chromaCrop.lastPixel = pInputData->pStripeConfig->pStripeOutput->outCrop_1to16_C.lastOut * 2 + 1;

                lumaCrop.firstLine   = 0;
                lumaCrop.lastLine    = Utils::AlignGeneric32(pInputData->pStripeConfig->stateDS[DS16Output].MNDSOutput.height,
                    DS4Factor * 2 ) / DS4Factor - 1;
                chromaCrop.firstLine = 0;
                chromaCrop.lastLine  = ((lumaCrop.lastLine + 1) / 2)  - 1;
            }
            else if (PixelRawOutput == m_output)
            {
                // already configured
            }
            else
            {
                CAMX_ASSERT_ALWAYS_MESSAGE("Invalid IFE output path!");
            }
        }
    }

    if (PixelRawOutput != m_output)
    {
        result = ModifyCropWindow(pInputData->pStripeConfig->cropType);
    }

    if ((CamxResultSuccess == result) &&
        (PixelRawOutput != m_output))
    {
        result = CalculateCropInfo(&lumaCrop,
                                   &chromaCrop,
                                   pInputData->pStripeConfig->cropType);
    }

    if (FullOutput == m_output)
    {
        pInputData->pCalculatedData->fullOutCrop = lumaCrop;
    }

    // FD ouput path configuration
    if ((CamxResultSuccess == result) &&
        (FDOutput == m_output))
    {
        pInputData->pCalculatedData->fdOutCrop = lumaCrop;
        ConfigureFDLumaRegisters(&lumaCrop);
        ConfigureFDChromaRegisters(&chromaCrop);
    }

    // Full output path configuration
    if ((CamxResultSuccess == result) &&
        FullOutput == m_output)
    {
        ConfigureFullLumaRegisters(&lumaCrop);
        ConfigureFullChromaRegisters(&chromaCrop);
    }

    if ((CamxResultSuccess == result) &&
        (DS4Output == m_output))
    {
        ConfigureDS4LumaRegisters(&lumaCrop);
        ConfigureDS4ChromaRegisters(&chromaCrop);
    }

    if ((CamxResultSuccess == result) &&
        (DS16Output == m_output))
    {
        ConfigureDS16LumaRegisters(&lumaCrop);
        ConfigureDS16ChromaRegisters(&chromaCrop);
    }

    if ((CamxResultSuccess == result) &&
        (PixelRawOutput == m_output))
    {
        ConfigurePixelRawRegisters(&lumaCrop);
    }

/// @todo (CAMX-666) replace #if DEBUG with set properties
#if DEBUG
    DumpRegConfig();
#endif // DEBUG

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::UpdateIFEInternalData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IFECrop10::UpdateIFEInternalData(
    ISPInputData* pInputData)
{
    IFEModuleEnableConfig* pCropModuleEnable = &pInputData->pCalculatedData->moduleEnable;

    switch (m_output)
    {
        case FDOutput:
            pCropModuleEnable->FDLumaCropRoundClampConfig.bitfields.CROP_EN     = m_moduleEnable;
            pCropModuleEnable->FDChromaCropRoundClampConfig.bitfields.CROP_EN   = m_moduleEnable;
            pInputData->pCalculatedData->metadata.cropInfo.FDPath               = m_pState->cropInfo;
            pInputData->pCalculatedData->metadata.appliedCropInfo.FDPath        = m_pState->appliedCropInfo;
            break;

        case FullOutput:
            pCropModuleEnable->fullLumaCropRoundClampConfig.bitfields.CROP_EN     = m_moduleEnable;
            pCropModuleEnable->fullChromaCropRoundClampConfig.bitfields.CROP_EN   = m_moduleEnable;
            pInputData->pCalculatedData->metadata.cropInfo.fullPath               = m_pState->cropInfo;
            pInputData->pCalculatedData->metadata.appliedCropInfo.fullPath        = m_pState->appliedCropInfo;


            if (CropTypeCentered == pInputData->pStripeConfig->cropType)
            {
                // In single IFE, use post-crops coordinates for DS4 precrop
                pInputData->pCalculatedData->preCropInfo.YCrop.firstLine    =
                m_regCmd.fullLuma.lineConfig.bitfields.FIRST_LINE;
                pInputData->pCalculatedData->preCropInfo.YCrop.lastLine     =
                    m_regCmd.fullLuma.lineConfig.bitfields.LAST_LINE;
                pInputData->pCalculatedData->preCropInfo.YCrop.firstPixel   =
                    m_regCmd.fullLuma.pixelConfig.bitfields.FIRST_PIXEL;
                pInputData->pCalculatedData->preCropInfo.YCrop.lastPixel    =
                    m_regCmd.fullLuma.pixelConfig.bitfields.LAST_PIXEL;
                pInputData->pCalculatedData->preCropInfo.CrCrop.firstLine   =
                    m_regCmd.fullChroma.lineConfig.bitfields.FIRST_LINE;
                pInputData->pCalculatedData->preCropInfo.CrCrop.lastLine    =
                    m_regCmd.fullChroma.lineConfig.bitfields.LAST_LINE;
                pInputData->pCalculatedData->preCropInfo.CrCrop.firstPixel  =
                    m_regCmd.fullChroma.pixelConfig.bitfields.FIRST_PIXEL;
                pInputData->pCalculatedData->preCropInfo.CrCrop.lastPixel   =
                    m_regCmd.fullChroma.pixelConfig.bitfields.LAST_PIXEL;
            }
            else
            {
                // When not overwriting, we need to calculate pre-crop properly to respect DS4,
                // but for now, we know we will overwrite so only do rudimentary values.
                pInputData->pCalculatedData->preCropInfo.YCrop.firstLine    =
                m_regCmd.fullLuma.lineConfig.bitfields.FIRST_LINE;
                pInputData->pCalculatedData->preCropInfo.YCrop.lastLine     =
                    m_regCmd.fullLuma.lineConfig.bitfields.LAST_LINE;
                pInputData->pCalculatedData->preCropInfo.YCrop.firstPixel   = 0;
                pInputData->pCalculatedData->preCropInfo.YCrop.lastPixel    = m_pState->scalerOutput.dimension.width - 1;
                pInputData->pCalculatedData->preCropInfo.CrCrop.firstLine   =
                    m_regCmd.fullChroma.lineConfig.bitfields.FIRST_LINE;
                pInputData->pCalculatedData->preCropInfo.CrCrop.lastLine    =
                    m_regCmd.fullChroma.lineConfig.bitfields.LAST_LINE;
                pInputData->pCalculatedData->preCropInfo.CrCrop.firstPixel  = 0;
                pInputData->pCalculatedData->preCropInfo.CrCrop.lastPixel   = m_pState->scalerOutput.dimension.width - 1;

                // In dual IFE, overlap is cropped in post-crop so we don't use it for pre-crop yet.
                if (TRUE == pInputData->pStripeConfig->overwriteStripes)
                {
                    pInputData->pCalculatedData->preCropInfo.YCrop.firstPixel =
                        pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to4_Y.firstOut;
                    pInputData->pCalculatedData->preCropInfo.YCrop.lastPixel  =
                        pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to4_Y.lastOut;

                    pInputData->pCalculatedData->preCropInfo.CrCrop.firstPixel =
                        pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to4_C.firstOut * 2;
                    pInputData->pCalculatedData->preCropInfo.CrCrop.lastPixel  =
                        pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to4_C.lastOut * 2 + 1;
                }
            }
            break;

        case DS4Output:
            pCropModuleEnable->DS4LumaCropRoundClampConfig.bitfields.CROP_EN     = m_moduleEnable;
            pCropModuleEnable->DS4ChromaCropRoundClampConfig.bitfields.CROP_EN   = m_moduleEnable;
            pInputData->pCalculatedData->metadata.cropInfo.DS4Path               = m_pState->cropInfo;
            pInputData->pCalculatedData->metadata.appliedCropInfo.DS4Path        = m_pState->appliedCropInfo;

            // When not overwriting, we need to calculate pre-crop properly to respect DS4,
            // but for now, we know we will overwrite so only do rudimentary values.
            pInputData->pCalculatedData->preCropInfoDS16.YCrop.firstLine    =
                m_regCmd.DS4Luma.lineConfig.bitfields.FIRST_LINE;
            pInputData->pCalculatedData->preCropInfoDS16.YCrop.lastLine     =
                m_regCmd.DS4Luma.lineConfig.bitfields.LAST_LINE;
            pInputData->pCalculatedData->preCropInfoDS16.YCrop.firstPixel   = 0;
            pInputData->pCalculatedData->preCropInfoDS16.YCrop.lastPixel    = m_pState->scalerOutput.dimension.width - 1;
            pInputData->pCalculatedData->preCropInfoDS16.CrCrop.firstLine   =
                m_regCmd.DS4Chroma.lineConfig.bitfields.FIRST_LINE;
            pInputData->pCalculatedData->preCropInfoDS16.CrCrop.lastLine    =
                m_regCmd.DS4Chroma.lineConfig.bitfields.LAST_LINE;
            pInputData->pCalculatedData->preCropInfoDS16.CrCrop.firstPixel  = 0;
            pInputData->pCalculatedData->preCropInfoDS16.CrCrop.lastPixel   = m_pState->scalerOutput.dimension.width - 1;

            if (TRUE == pInputData->pStripeConfig->overwriteStripes)
            {
                pInputData->pCalculatedData->preCropInfoDS16.YCrop.firstPixel =
                    pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to16_Y.firstOut;
                pInputData->pCalculatedData->preCropInfoDS16.YCrop.lastPixel  =
                    pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to16_Y.lastOut;

                pInputData->pCalculatedData->preCropInfoDS16.CrCrop.firstPixel =
                    pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to16_C.firstOut * 2;
                pInputData->pCalculatedData->preCropInfoDS16.CrCrop.lastPixel  =
                    pInputData->pStripeConfig->pStripeOutput->preDS4Crop_1to16_C.lastOut * 2 + 1;
            }

            // PostCrop is disabled if PreCrop is enabled
            if (CropTypeCentered == pInputData->pStripeConfig->cropType)
            {
                if (0 == pCropModuleEnable->frameProcessingModuleConfig.bitfields.DS_4TO1_1ST_PRE_CROP_EN)
                {
                    pCropModuleEnable->frameProcessingModuleConfig.bitfields.DS_4TO1_1ST_POST_CROP_EN = m_moduleEnable;
                }
            }
            else
            {
                pCropModuleEnable->frameProcessingModuleConfig.bitfields.DS_4TO1_1ST_POST_CROP_EN = m_moduleEnable;
            }
            break;

        case DS16Output:
            pCropModuleEnable->DS16LumaCropRoundClampConfig.bitfields.CROP_EN     = m_moduleEnable;
            pCropModuleEnable->DS16ChromaCropRoundClampConfig.bitfields.CROP_EN   = m_moduleEnable;
            pInputData->pCalculatedData->metadata.cropInfo.DS16Path               = m_pState->cropInfo;
            pInputData->pCalculatedData->metadata.appliedCropInfo.DS16Path        = m_pState->appliedCropInfo;

            // PostCrop is disabled if PreCrop is enabled
            if (CropTypeCentered == pInputData->pStripeConfig->cropType)
            {
                if (0 == pCropModuleEnable->frameProcessingModuleConfig.bitfields.DS_4TO1_1ST_PRE_CROP_EN)
                {
                    pCropModuleEnable->frameProcessingModuleConfig.bitfields.DS_4TO1_2ND_POST_CROP_EN = m_moduleEnable;
                }
            }
            else
            {
                pCropModuleEnable->frameProcessingModuleConfig.bitfields.DS_4TO1_2ND_POST_CROP_EN = m_moduleEnable;
            }
            break;

        default:
            CAMX_ASSERT_ALWAYS_MESSAGE("Invalid format %d", m_output);
            break;
    }

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::Execute
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::Execute(
    ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(static_cast<UINT>(m_modulePath) < IFEMaxNonCommonPaths);
    if (NULL != pInputData)
    {
        m_pState        = &pInputData->pStripeConfig->stateCrop[static_cast<UINT>(m_modulePath)];
    }
    m_pInputData    = pInputData;

    if (NULL != pInputData)
    {
        // Check if dependent is valid and been updated compared to last request
        result = ValidateDependenceParams(pInputData);
        if (CamxResultSuccess == result)
        {
            if (TRUE == CheckDependenceChange(pInputData))
            {
                result = RunCalculation(pInputData);

                if (CamxResultSuccess == result)
                {
                    result = CreateCmdList(pInputData);
                }
            }
            if (CamxResultSuccess == result)
            {
                UpdateIFEInternalData(pInputData);
            }
        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Input pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::PrepareStripingParameters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IFECrop10::PrepareStripingParameters(
    ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(static_cast<UINT>(m_modulePath) < IFEMaxNonCommonPaths);

    if (NULL != pInputData)
    {
        m_pState     = &pInputData->pStripeConfig->stateCrop[static_cast<UINT>(m_modulePath)];
        m_pInputData = pInputData;
        // Check if dependent is valid and been updated compared to last request
        result = ValidateDependenceParams(pInputData);
        if (CamxResultSuccess == result)
        {

            if (TRUE == CheckDependenceChange(pInputData))
            {
                result = RunCalculation(pInputData);
            }

            if (CamxResultSuccess == result)
            {
                UpdateIFEInternalData(pInputData);
            }

        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Input pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IFECrop10::IFECrop10
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
IFECrop10::IFECrop10(
    IFEModuleCreateData* pCreateData)
{
    m_type              = ISPIQModuleType::IFECrop;
    m_moduleEnable      = TRUE;
    m_32bitDMILength    = 0;
    m_64bitDMILength    = 0;

    m_cmdLength =
        PacketBuilder::RequiredWriteRegRangeSizeInDwords(sizeof(IFECrop10FDLumaReg) / RegisterWidthInBytes) +
        PacketBuilder::RequiredWriteRegRangeSizeInDwords(sizeof(IFECrop10FDChromaReg) / RegisterWidthInBytes) +
        PacketBuilder::RequiredWriteRegRangeSizeInDwords(sizeof(IFECrop10FullLumaReg) / RegisterWidthInBytes) +
        PacketBuilder::RequiredWriteRegRangeSizeInDwords(sizeof(IFECrop10FullChromaReg) / RegisterWidthInBytes) +
        PacketBuilder::RequiredWriteRegRangeSizeInDwords(sizeof(IFECropPixelRawReg) / RegisterWidthInBytes);

    m_modulePath = pCreateData->pipelineData.IFEPath;
    m_output     = FullOutput;
    switch (m_modulePath)
    {
        case IFEPipelinePath::FDPath:
            m_output = FDOutput;
            break;

        case IFEPipelinePath::FullPath:
            m_output = FullOutput;
            break;

        case IFEPipelinePath::DS4Path:
            m_output = DS4Output;
            break;

        case IFEPipelinePath::DS16Path:
            m_output = DS16Output;
            break;

        case IFEPipelinePath::PixelRawDumpPath:
            m_output = PixelRawOutput;
            break;

        default:
            CAMX_ASSERT_ALWAYS_MESSAGE("Invalid path");
            break;
    }

    pCreateData->initializationData.pStripeConfig->stateCrop[static_cast<UINT>(m_modulePath)].cropWindow.left   = 1;
    pCreateData->initializationData.pStripeConfig->stateCrop[static_cast<UINT>(m_modulePath)].cropWindow.top    = 1;
    pCreateData->initializationData.pStripeConfig->stateCrop[static_cast<UINT>(m_modulePath)].cropWindow.width  = 1;
    pCreateData->initializationData.pStripeConfig->stateCrop[static_cast<UINT>(m_modulePath)].cropWindow.height = 1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IFECrop10::~IFECrop10
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
IFECrop10::~IFECrop10()
{
}

CAMX_NAMESPACE_END
